package com.nl.idempotent.dao;

import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DataAccessException;
import org.springframework.dao.EmptyResultDataAccessException;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;
import org.springframework.util.ObjectUtils;

/**
 * 幂等操作Mapper.
 *
 * @author breggor
 */
@Slf4j
@Transactional(rollbackFor = RuntimeException.class)
public class IdempotentDaoImpl implements IdempotentDao, InitializingBean {

    //表结构
    private static final String INIT_SQL = "CREATE TABLE IF NOT EXISTS `idempotent` (\n"
        + "  `id` varchar(80) NOT NULL COMMENT '幂等业务标志',\n"
        + "  `module` varchar(20) NOT NULL COMMENT '模块名',\n"
        + "  `request_id` bigint(20) DEFAULT NULL COMMENT '请求ID',\n"
        + "  `result` longtext COMMENT '业务结果',\n"
        + "  `create_time` timestamp(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) COMMENT '创建时间',\n"
        + "  `update_time` timestamp(6) NULL DEFAULT NULL COMMENT '更新时间',\n"
        + "  PRIMARY KEY (`id`)\n"
        + ") ENGINE=InnoDB AUTO_INCREMENT=18 DEFAULT CHARSET=utf8mb4";

    private static final String INSERT = "INSERT INTO idempotent (id, `module`) VALUES (?, ?)";

    private static final String SELECT_RESULT_WHERE_ID = "SELECT result FROM idempotent WHERE id = ?";

    private static final String UPDATE_RESULT_WHERE_ID = "UPDATE idempotent SET result=?, update_time=CURRENT_TIMESTAMP(6) WHERE id = ?";

    private static final String IS_EXIST_WHERE_ID = "SELECT 1 FROM idempotent WHERE id = ? LIMIT 1";

    @Autowired
    private JdbcTemplate jdbcTemplate;

    @Override
    public void afterPropertiesSet() throws Exception {
        log.info("幂等--初始化表结构");
        try {
            jdbcTemplate.execute(INIT_SQL);
        } catch (DataAccessException e) {
            log.error("幂等--表结构初始化失败", e);
            throw new RuntimeException(e);
        }
    }

    @Override
    public String load(final String id) {
        Assert.notNull(id, "幂等Id不能为空");
        try {
            return jdbcTemplate.queryForObject(SELECT_RESULT_WHERE_ID, String.class, id);
        } catch (EmptyResultDataAccessException e) {
            //ignore没有查到数据
        }
        return null;
    }

    @Override
    @Transactional(propagation = Propagation.NESTED, rollbackFor = RuntimeException.class)
    public boolean begin(final String id, final String module) {
        Assert.notNull(id, "幂等Id不能为空");
        int affect = jdbcTemplate.update(INSERT, id, module);
        return affect > 0;
    }

    @Override
    public void end(final String id, final String result) {
        Assert.notNull(id, "幂等Id不能为空");
        Assert.notNull(result, "业务结果不能为空");
        jdbcTemplate.update(UPDATE_RESULT_WHERE_ID, result, id);
    }

    @Override
    public boolean isProcessed(final String id) {
        Assert.notNull(id, "幂等Id不能为空");
        try {
            return !ObjectUtils.isEmpty(jdbcTemplate.queryForObject(IS_EXIST_WHERE_ID, String.class, id));
        } catch (EmptyResultDataAccessException e) {
            //ignore没有查到数据
        }
        return false;
    }
}
